
/**
 * @brief Software SPI driver
 * 
 * Timing standard: SPI Mode 0 (CPOL=0, CPHA=0)
 * - Clock idle low
 * - Data changed on falling edge, sampled on rising edge
 * - MSB first
 * - CS active low
 * 
 * SCK frequency ≈ 1 / (2 * spi_delay_time)
 * 
 * Example timing:
 *   SCK  : __/‾\__/‾\__/‾\__/‾\__/‾\__/‾\__/‾\__/‾\__
 *   MOSI : ---<D7>---<D6>---<D5>--- ... ---<D0>---
 *   MISO : ---<Rx7>--<Rx6>--<Rx5>-- ... --<Rx0>---
 *   CS   : ‾‾‾‾‾‾\__________________________/‾‾‾‾‾
 */

#include "t_spi_m.h"

t_spi_t *t_spi_create(void *arg,
                      T_SPI_NSS_Mode mode,
                      Tspi_pincfg_func cfgout_func,
                      Tspi_pincfg_func cfgin_func,
                      Tspi_delay_func delay_func,
                      Tspi_write_func spi_w_func,
                      Tspi_read_func spi_r_func,
                      const t_spi_gpio_t *hw_gpio)
{
    if (!cfgout_func || !cfgin_func || !delay_func || !spi_w_func || !spi_r_func || !hw_gpio) {
        return T_NULL;
    }

    t_spi_t *spi = (t_spi_t *)T_MALLOC(sizeof(t_spi_t));
    if (!spi) return T_NULL;

    memset(spi, 0, sizeof(t_spi_t));

    spi->arg = arg;
    spi->hwcfgOut_func = cfgout_func;
    spi->hwcfgIn_func = cfgin_func;
    spi->delay_func = delay_func;
    spi->write_func = spi_w_func;
    spi->read_func = spi_r_func;
    spi->hw_gpio = hw_gpio;

    spi->T_SPI_NSS_FBIT = (mode == T_SPI_NSS_Soft) ? 1 : 0;

    // 配置GPIO
    cfgout_func(hw_gpio->sck_port, hw_gpio->sck_pin);
    cfgout_func(hw_gpio->mosi_port, hw_gpio->mosi_pin);
    cfgin_func(hw_gpio->miso_port, hw_gpio->miso_pin);
    if (spi->T_SPI_NSS_FBIT) {
        cfgout_func(hw_gpio->cs_port, hw_gpio->cs_pin);
        spi_w_func(hw_gpio->cs_port, hw_gpio->cs_pin, T_SPI_PIN_SET);
    }

    return spi;
}

static void t_spi_cs_low(t_spi_t *me)
{
    if (me->T_SPI_NSS_FBIT) {
        me->write_func(me->hw_gpio->cs_port, me->hw_gpio->cs_pin, T_SPI_PIN_RESET);
        me->delay_func(spi_delay_time);
    }
}

static void t_spi_cs_high(t_spi_t *me)
{
    if (me->T_SPI_NSS_FBIT) {
        me->write_func(me->hw_gpio->cs_port, me->hw_gpio->cs_pin, T_SPI_PIN_SET);
        me->delay_func(spi_delay_time);
    }
}

spi_err_t t_spi_send_byte(t_spi_t *me, uint8_t *tdata, uint16_t tlen, uint16_t ttimeout_ms) {
    if (!me || !tdata || tlen == 0) return SPI_ERR_NULL_PTR;

    t_spi_cs_low(me);  // 拉低 CS

    for (uint16_t i = 0; i < tlen; i++) {
        uint8_t byte = tdata[i];

        for (int j = 7; j >= 0; j--) {
            // CPOL=0, CPHA=0: SCK 先低
            me->write_func(me->hw_gpio->sck_port, me->hw_gpio->sck_pin, T_SPI_PIN_RESET);

            // 下降沿之后 设置 MOSI（数据变化）
            me->write_func(me->hw_gpio->mosi_port, me->hw_gpio->mosi_pin, 
                          (byte & (1 << j)) ? T_SPI_PIN_SET : T_SPI_PIN_RESET);

            me->delay_func(spi_delay_time);  // 给 MOSI 足够建立时间

            // 上升沿时采样（对方采样）
            me->write_func(me->hw_gpio->sck_port, me->hw_gpio->sck_pin, T_SPI_PIN_SET);
            me->delay_func(spi_delay_time);  // 保持时钟高电平
        }
    }

    // 拉高 CS
    t_spi_cs_high(me);
    return SPI_OK;
}


spi_err_t t_spi_read_byte(t_spi_t *me, uint8_t *rdata, uint16_t rlen, uint16_t rtimeout_ms)
{
    if (!me || !rdata || rlen == 0) return SPI_ERR_NULL_PTR;

    t_spi_cs_low(me);

    for (uint16_t i = 0; i < rlen; i++) {
        uint8_t byte = 0;
        for (int j = 0; j < 8; j++) {
            // 拉低 SCK
            me->write_func(me->hw_gpio->sck_port, me->hw_gpio->sck_pin, T_SPI_PIN_RESET);
            me->delay_func(spi_delay_time);

            // 拉高 SCK 后读 MISO
            me->write_func(me->hw_gpio->sck_port, me->hw_gpio->sck_pin, T_SPI_PIN_SET);
            me->delay_func(spi_delay_time);

            byte <<= 1;
            if (me->read_func(me->hw_gpio->miso_port, me->hw_gpio->miso_pin)) {
                byte |= 0x01;
            }
        }
        rdata[i] = byte;
    }

    t_spi_cs_high(me);
    return SPI_OK;
}

spi_err_t t_spi_SendRead_byte(t_spi_t *me, uint8_t *tdata, uint8_t *rdata, uint16_t len, uint16_t timeout_ms) {
    if (!me || len == 0) return SPI_ERR_NULL_PTR;

    t_spi_cs_low(me);

    for (uint16_t i = 0; i < len; i++) {
        uint8_t tx = tdata ? tdata[i] : 0xFF;
        uint8_t rx = 0;

        for (int j = 7; j >= 0; j--) {
            // SCK LOW
            me->write_func(me->hw_gpio->sck_port, me->hw_gpio->sck_pin, T_SPI_PIN_RESET);

            // 设置 MOSI
            me->write_func(me->hw_gpio->mosi_port, me->hw_gpio->mosi_pin, 
                          (tx & (1 << j)) ? T_SPI_PIN_SET : T_SPI_PIN_RESET);

            me->delay_func(spi_delay_time);  // 给对方准备时间

            // SCK HIGH：采样 MISO
            me->write_func(me->hw_gpio->sck_port, me->hw_gpio->sck_pin, T_SPI_PIN_SET);

            rx <<= 1;
            if (me->read_func(me->hw_gpio->miso_port, me->hw_gpio->miso_pin)) {
                rx |= 0x01;
            }

            me->delay_func(spi_delay_time);  // 保持时钟高电平
        }

        if (rdata) rdata[i] = rx;
    }

    t_spi_cs_high(me);
    return SPI_OK;
}

